 #include"myheadd.h"


extern Friend *friend_list;
extern pthread_mutex_t list_mutex;
extern unsigned short my_private_port;


typedef struct // 私聊发送线程所需数据结构
{
    int sockfd;   //自己的套接字
    struct sockaddr_in target_addr; //对方的套接字
    char my_name[20];
    char friend_name[20];
} SendData;

// 线程清理函数：关闭套接字
void cleanup_socket(void *arg) 
{
    int *sockfd = (int *)arg;
    if (*sockfd != -1) 
    {
        close(*sockfd);
        *sockfd = -1;
        printf("套接字已关闭\n");
    }
}


void *private_chat_listener(void *arg) // 第一次是全局私聊监听线程：始终监听自己的私聊端口  第二次修改:我直接改成接收消息的线程
{
if (!arg) 
    {
        fprintf(stderr, "用户名参数为空\n");
        return NULL;
    }
    char *my_name = (char *)arg;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) 
    {
        perror("创建监听套接字失败");
        return NULL;
    }
    // 设置端口复用
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) 
    {
        perror("设置端口复用失败");
        close(sockfd);
        return NULL;
    }
    // 绑定到自己的私聊端口
    struct sockaddr_in my_addr;
    memset(&my_addr, 0, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(my_private_port);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) 
    {
        perror("绑定监听套接字失败");
        close(sockfd);
        return NULL;
    }
    // 注册线程清理函数
    pthread_cleanup_push(cleanup_socket, &sockfd);
    char buffer[300];
    struct sockaddr_in from_addr;
    socklen_t addr_len = sizeof(from_addr);
    while (1) 
    {
        pthread_testcancel();// 允许线程被取消      
        memset(buffer, 0, sizeof(buffer));
        ssize_t recv_len = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0,
                                   (struct sockaddr *)&from_addr, &addr_len);
    
        
        if (recv_len < 0) 
        {
            perror("接收私聊消息失败");
            continue;
        }
        buffer[recv_len] = '\0';

        // 解析消息格式：[私聊]发送者:消息内容
        char *prefix = "[私聊]";
        if (strncmp(buffer, prefix, strlen(prefix)) != 0) 
        {
            printf("收到无效消息: %s\n", buffer);
            continue;
        }
        char *sender = buffer + strlen(prefix);//用户名
        char *msg = strchr(sender, ':');//发的消息
        if (!msg) 
        {
            printf("消息格式错误: %s\n", buffer);
            continue;
        }
        *msg = '\0';  // 分割发送者和消息
        msg++;        // 指向消息内容


         ////聊天记录
        write_history(PRIVATE_CHAT, sender, my_name, msg);
        ////聊天记录

        // 只提示收到消息，不直接回复，引导用户通过主菜单2号功能进入聊天
        printf("\n[新消息---->ps:首次回复,需要按2进入聊天室<-----]来自 %s: %s\n", sender, msg);
        printf("\n[回复]:");
        fflush(stdout);  // 确保提示信息及时输出
    }
    // 线程清理（与push配对）
    pthread_cleanup_pop(1);
    return NULL;
}
void *private_chat_send(void *arg) // 私聊------发送线程（主动发起方使用）
{
    if (!arg) 
    {
        fprintf(stderr, "发送参数为空\n");
        return NULL;
    }
    SendData *data = (SendData *)arg;
    int sockfd = data->sockfd;

    
    pthread_cleanup_push(cleanup_socket, &sockfd);// 注册线程清理函数（防止线程自己莫名其妙退出）

    char message[256];
    while (1) 
    {
        pthread_testcancel();  // 允许线程被取消
        printf("[%s->%s]: ", data->my_name, data->friend_name);
        fflush(stdout);

        if (fgets(message, sizeof(message), stdin) == NULL) 
        {
            printf("输入失败\n");
            break;
        }
        message[strcspn(message, "\n")] = 0;  // 去除换行符  strcspn就是返回  5（例如  hello\n）;

        if (strcmp(message, "exit") == 0) 
        {
            break;
        }

        char full_message[300];
        snprintf(full_message, sizeof(full_message), "[私聊]%s:%s", data->my_name, message);
        
        sendto(sockfd, full_message, strlen(full_message), 0,
              (struct sockaddr *)&data->target_addr, sizeof(data->target_addr));
        ////聊天记录
        write_history(PRIVATE_CHAT, data->my_name, data->friend_name, message);
        ////聊天记录
    }

    pthread_cleanup_pop(1);  //与pthread_cleanup_push配对，为什么参数是1，因为保证线程是正常退出还是被取消，都执行清理函数cleanup_socket。然后套接字都会被关闭。
    return NULL;
}


void start_private_chat(char *friend_name, char *my_name) // 启动私聊（主动发起方）
{
//第一次,主线程，只有发线程，没有收线程，原本的void *private_chat_listener(void *arg)，无法完成收取功能。
    if (!friend_name || !my_name || strcmp(friend_name, my_name) == 0) 
    {
        printf("无效的用户名(亦或者是给自己发消息)\n");
        return;
    }
    pthread_mutex_lock(&list_mutex);
    Friend *target = NULL;
    bool found = false;
    list_for_each_entry(target, &friend_list->list, list) 
    {
        if (strcmp(target->user_name, friend_name) == 0) 
        {
            found = true;
            break;
        }
    }
    if (!found) 
    {
        printf("没有找到好友 %s\n", friend_name);
        pthread_mutex_unlock(&list_mutex);
        return;
    }
    char target_ip[16];        // 复制目标信息（解锁前完成，避免数据竞争）
    unsigned short target_port;
    strncpy(target_ip, target->ip, sizeof(target_ip)-1);//复制IP
    target_port = target->private_port; //复制私聊端口
    pthread_mutex_unlock(&list_mutex);//解锁好友列表
    
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) 
        {
            perror("创建套接字失败");
            return;
        }

    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) // 设置端口复用
    {
        perror("设置端口复用失败");
        close(sockfd);
        return;
    }
   
    struct sockaddr_in my_addr;
    memset(&my_addr, 0, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(0); // 自动分配端口
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) 
    {
        perror("绑定套接字失败");
        close(sockfd);
        return;
    }

    struct sockaddr_in target_addr;
    memset(&target_addr, 0, sizeof(target_addr));
    target_addr.sin_family = AF_INET;
    target_addr.sin_port = htons(target_port);
    target_addr.sin_addr.s_addr = inet_addr(target_ip);
    printf("开始与 %s 私聊 (输入'exit'退出)\n", friend_name);


    SendData *send_data = malloc(sizeof(SendData));// 准备发送线程数据
    if (!send_data) 
    {
        perror("内存分配失败");
        close(sockfd);
        return;
    }
    
    send_data->sockfd = sockfd;
    send_data->target_addr = target_addr;
    strncpy(send_data->my_name, my_name, sizeof(send_data->my_name)-1);
    strncpy(send_data->friend_name, friend_name, sizeof(send_data->friend_name)-1);
   
    pthread_t send_tid; // 创建发送线程
    if (pthread_create(&send_tid, NULL, private_chat_send, send_data) != 0) 
    {
        perror("创建发送线程失败");
        free(send_data);
        close(sockfd);
        return;
    }
    // 等待发送线程结束
    pthread_join(send_tid, NULL);
    // 清理资源
    free(send_data);
    close(sockfd);  // 双重保险，线程内已通过清理函数关闭
    printf("1对1聊天结束\n");
}
